"""
@file: Auth.py
@author: lingdubing
@time: 2024/11/08  9:48
@desc: 安全验证类
@character: utf-8
"""
from typing import Optional
import jwt
import datetime
from fastapi import Header, HTTPException, Depends, Request
from schemas.response import STATUS_CODE
from config import settings
from sqlmodel import Session, select
from database.mysql import engine
from models.models import Access, User, UserRoleLink, RoleAccessLink


# 基于电话号码和时间戳生成验证token
def generate_token(phone: str):
    timestamp = int(datetime.datetime.utcnow().timestamp())  # 当前时间戳
    # 计算过期时间戳（秒级）
    expiration_time = datetime.datetime.utcnow() + datetime.timedelta(minutes=60)  # 当前时间戳加上一小时

    data = {'username': phone, 'exp': expiration_time, 'timestamp': timestamp}

    token = jwt.encode(payload=data, key=settings.JWT_SECRET_KEY, algorithm=settings.JWT_ALGORITHM)
    return token


async def verify_token(authorization: Optional[str] = Header(None)):
    """
    token验证
    :param authorization:
    :return:
    """
    # ----------------------------------------验证JWT token------------------------------------------------------------

    try:
        if not authorization:
            credentials_exception = HTTPException(
                status_code=STATUS_CODE["unauthorized"],
                detail="无效凭证",
                headers={"WWW-Authenticate": f"Bearer"},
            )
            raise credentials_exception

        if not authorization.startswith("Bearer "):
            credentials_exception = HTTPException(
                status_code=STATUS_CODE["unauthorized"],
                detail="无效凭证",
                headers={"WWW-Authenticate": f"Bearer"},
            )
            raise credentials_exception

        ver_token = authorization[7:]

        # token解密
        payload = jwt.decode(
            ver_token,
            settings.JWT_SECRET_KEY,
            algorithms=[settings.JWT_ALGORITHM]
        )

        if payload:
            # 用户电话号
            username = payload.get("username", None)
            # 无效用户信息
            if username is None:
                credentials_exception = HTTPException(
                    status_code=STATUS_CODE["unauthorized"],
                    detail="无效凭证",
                    headers={"WWW-Authenticate": f"Bearer{ver_token}"},
                )
                raise credentials_exception
            else:
                with Session(engine) as session:
                    user_info = session.exec(select(User).where(User.username == username)).first()
                    if user_info.user_status == 0 or user_info is None:
                        credentials_exception = HTTPException(
                            status_code=STATUS_CODE["unauthorized"],
                            detail="无效凭证,无用户信息",
                            headers={"WWW-Authenticate": f"Bearer{ver_token}"},
                        )
                        raise credentials_exception
                    user_role = session.exec(
                        select(UserRoleLink).where(UserRoleLink.user_id == user_info.user_id)).first()
                    if user_role is None:
                        credentials_exception = HTTPException(
                            status_code=STATUS_CODE["unauthorized"],
                            detail="无效凭证，无角色分配",
                            headers={"WWW-Authenticate": f"Bearer{ver_token}"},
                        )
                        raise credentials_exception
                    return user_role.role_id
        else:
            credentials_exception = HTTPException(
                status_code=STATUS_CODE["unauthorized"],
                detail="无效凭证",
                headers={"WWW-Authenticate": f"Bearer {ver_token}"},
            )
            raise credentials_exception

    except jwt.ExpiredSignatureError:

        raise HTTPException(
            status_code=STATUS_CODE["unauthorized"],
            detail="凭证已证过期",
            headers={"WWW-Authenticate": f"Bearer {ver_token}"},
        )

    except jwt.InvalidTokenError:

        raise HTTPException(
            status_code=STATUS_CODE["unauthorized"],
            detail="无效凭证",
            headers={"WWW-Authenticate": f"Bearer {ver_token}"},
        )


async def get_id_by_token(authorization: Optional[str] = Header(None)):
    """
    根据token查找用户id
    :param authorization:
    :return:
    """
    # ----------------------------------------验证JWT token------------------------------------------------------------

    try:
        if not authorization:
            credentials_exception = HTTPException(
                status_code=STATUS_CODE["unauthorized"],
                detail="无效凭证",
                headers={"WWW-Authenticate": f"Bearer"},
            )
            raise credentials_exception

        if not authorization.startswith("Bearer "):
            credentials_exception = HTTPException(
                status_code=STATUS_CODE["unauthorized"],
                detail="无效凭证",
                headers={"WWW-Authenticate": f"Bearer"},
            )
            raise credentials_exception

        ver_token = authorization[7:]

        # token解密
        payload = jwt.decode(
            ver_token,
            settings.JWT_SECRET_KEY,
            algorithms=[settings.JWT_ALGORITHM]
        )

        if payload:
            # 用户电话号
            username = payload.get("username", None)
            # 无效用户信息
            if username is None:
                credentials_exception = HTTPException(
                    status_code=STATUS_CODE["unauthorized"],
                    detail="无效凭证",
                    headers={"WWW-Authenticate": f"Bearer{ver_token}"},
                )
                raise credentials_exception
            else:
                with Session(engine) as session:
                    user_info = session.exec(select(User).where(User.username == username)).first()
                    if user_info.user_status == 0 or user_info is None:
                        credentials_exception = HTTPException(
                            status_code=STATUS_CODE["unauthorized"],
                            detail="无效凭证,无用户信息",
                            headers={"WWW-Authenticate": f"Bearer{ver_token}"},
                        )
                        raise credentials_exception
                    user_role = session.exec(
                        select(UserRoleLink).where(UserRoleLink.user_id == user_info.user_id)).first()
                    if user_role is None:
                        credentials_exception = HTTPException(
                            status_code=STATUS_CODE["unauthorized"],
                            detail="无效凭证，无角色分配",
                            headers={"WWW-Authenticate": f"Bearer{ver_token}"},
                        )
                        raise credentials_exception
                    return user_info.user_id
        else:
            credentials_exception = HTTPException(
                status_code=STATUS_CODE["unauthorized"],
                detail="无效凭证",
                headers={"WWW-Authenticate": f"Bearer {ver_token}"},
            )
            raise credentials_exception

    except jwt.ExpiredSignatureError:

        raise HTTPException(
            status_code=STATUS_CODE["unauthorized"],
            detail="凭证已证过期",
            headers={"WWW-Authenticate": f"Bearer {ver_token}"},
        )

    except jwt.InvalidTokenError:

        raise HTTPException(
            status_code=STATUS_CODE["unauthorized"],
            detail="无效凭证",
            headers={"WWW-Authenticate": f"Bearer {ver_token}"},
        )


async def check_permission(request: Request, role_id: str = Depends(verify_token)):
    permission_name = request.url.path.split('/')[-1]
    with Session(engine) as session:
        user_access = session.exec(select(RoleAccessLink).where(RoleAccessLink.role_id == role_id)).all()
        access_scopes_list = []
        # 根据权限id查询权限信息
        for ral in user_access:
            access_info = session.exec(
                select(Access).where(Access.access_id == ral.access_id and Access.is_menu == True)).first()
            access_scopes_list.append(access_info.access_url)

        if permission_name not in access_scopes_list:
            credentials_exception = HTTPException(
                status_code=STATUS_CODE["unauthorized"],
                detail="没有权限",
                headers={"WWW-Authenticate": "Bearer"}
            )
            raise credentials_exception
